using System;
using System.Diagnostics;

namespace QuickBencher
{
    internal class BenchmarkRunner
    {
        private Stopwatch _stopwatch;
        
        private Action _action;
        private bool _withGarbageCollection;

        private TimeSpan _totalProcessorTimeStart;
        private TimeSpan _totalProcessorTimeEnd;
        private TimeSpan _userProcessorTimeStart;
        private TimeSpan _userProcessorTimeEnd;
        private TimeSpan _privilegedProcessorTimeStart;
        private TimeSpan _privilegedProcessorTimeEnd;
        
        public BenchmarkRunner(string label, Action action, bool withGarbageCollection)
        {
            _withGarbageCollection = withGarbageCollection;
            _action = action;
            Measurement = new Measurement { Label = label };
        }
     
        public Measurement Measurement { get; private set; }
        
        public void Execute()
        {
            var currentProcess = Process.GetCurrentProcess();

            if (_withGarbageCollection)
                GC.Collect();

            _stopwatch = Stopwatch.StartNew();
            _totalProcessorTimeStart = currentProcess.TotalProcessorTime;
            _userProcessorTimeStart = currentProcess.UserProcessorTime;
            _privilegedProcessorTimeStart = currentProcess.PrivilegedProcessorTime;

            _action();

            _totalProcessorTimeEnd = currentProcess.TotalProcessorTime;
            _userProcessorTimeEnd = currentProcess.UserProcessorTime;
            _privilegedProcessorTimeEnd = currentProcess.PrivilegedProcessorTime;
            _stopwatch.Stop();

            SaveRecordings();
        }

        private void SaveRecordings()
        {
            Measurement.UserTime = GetDuration(_userProcessorTimeStart, _userProcessorTimeEnd);
            Measurement.SystemTime = GetDuration(_privilegedProcessorTimeStart, _privilegedProcessorTimeEnd);
            Measurement.TotalTime = GetDuration(_totalProcessorTimeStart, _totalProcessorTimeEnd);
            Measurement.RealTime = _stopwatch.ElapsedMilliseconds / 1000d;
        }

        private static double GetDuration(TimeSpan start, TimeSpan end)
        {
            var executionDuration = end - start;
            return executionDuration.TotalMilliseconds / 1000d;
        }
    }
}
